/*
 *  Copyright 2008 Mark Ashworth <javameme@gmail.com>.
 * 
 *  Licensed under the GNU General Public License v3 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.gnu.org/licenses/gpl-3.0.html
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package xperiment.metaphor.model.xml;

import java.util.HashMap;
import java.util.Map;
import org.dom4j.Attribute;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import metaphor.persistence.xml.PersistentClassResolver;

/**
 * The DefaultPersistentClassResolver tries to resolve the name in the following
 * order: -
 * <ol>
 * <li>The element name is the persistent class name</li>
 * <li>The element has an attribute called class that has the persistent class</li>
 * <li>The name map contains a mapping between element name and the persistent class</li>
 * </ol>
 * <strong>1. Persistent class in element name</strong><br />
 * The following is an example of the XML fragment for this type of resolution:<br />
 * &lt;xperiment.metaphor.model.people.Gender name="MALE" /&gt;<br /><br />
 * 
 * <strong>2. Persistent class in element attribute</strong><br />
 * The following is an example of the XML fragment for this type of resolution:<br />
 * &lt;gender class="xperiment.metaphor.model.people.Gender" name="MALE" /&gt;<br /><br />
 * 
 * <strong>3. Persistent class in name map</strong><br />
 * The following is an example of the XML fragment for this type of resolution:<br />
 * &lt;gender name="MALE" /&gt;<br />
 * but the name map must have a entry for "gender", Gender.class<br />
 * 
 * @author Mark Ashworth <javameme@gmail.com>
 * @version 1.0.0
 */
public class DefaultPersistentClassResolver implements PersistentClassResolver {
    protected Logger logger = LoggerFactory.getLogger(getClass());
    /**
     * The map of names to persistent classes
     */
    private Map<String, Class> nameMap = new HashMap<String, Class>();
    /**
     * The map of names to persistent classes
     * @return Map<String, Class>
     */
    public Map<String, Class> getNameMap() {
        return nameMap;
    }
    /**
     * The map of names to persistent classes
     * @param nameMap The new value
     */
    public void setNameMap(Map<String, Class> nameMap) {
        this.nameMap = nameMap;
    }
    /**
     * Resolves the <i>name</i> to a persistent class that can be inserted, 
     * updated and retrieved from the database.
     * 
     * @param element The element to resolve to a persistent class
     * @return Class or null is the persistent class could not be resolved
     */
    public Class resolve(Element element) {
        if (element == null) {
            throw new IllegalArgumentException("The element is null");
        }
        Class clazz = resolveByElementName(element);
        if (clazz != null) {
            return clazz;
        }
        
        clazz = resolveByClassAttribute(element);
        if (clazz != null) {
            return clazz;
        }
        
        return resolveByPersistentClassMap(element);
    }
    
    /**
     * Resolves the persistent class by the element name being a valid 
     * Java class name
     * @param element The element to resolve
     * @return Class
     */
    protected Class resolveByElementName(Element element) {
        if (element == null) {
            throw new IllegalArgumentException("The element is null");
        }
        try {
            return Class.forName(element.getName());
        } catch (ClassNotFoundException ex) {
            logger.debug("Persistent class for {} not found on the classpath", element.getName());
        }
        return null;
    }
    
    /**
     * Resolves the persistent class by the element attribute 'class' being a valid 
     * Java class name
     * @param element The element to resolve
     * @return Class
     */
    protected Class resolveByClassAttribute(Element element) {
        if (element == null) {
            throw new IllegalArgumentException("The element is null");
        }
        try {
            Attribute attribute = element.attribute("class");
            if (attribute != null) {
                return Class.forName(attribute.getValue());
            } else {
                logger.debug("No class attribute found on element {}", element.getName());
            }
        } catch (ClassNotFoundException ex) {
            logger.debug("Persistent class for {} not found on the classpath", element.getName());
        }
        return null;
    }
    
    /**
     * Resolves the persistent class by the element name being a in the 
     * name map.
     * @param element The element to resolve
     * @return Class
     */
    protected Class resolveByPersistentClassMap(Element element) {
        if (element == null) {
            throw new IllegalArgumentException("The element is null");
        }
        return nameMap.get(element.getName());
    }
}
